package com.gw.youTubeAudioAPI.audioStream
{
	import com.gw.youTubeAudioAPI.decode.container.Decoder;
	
	import flash.events.SampleDataEvent;
	import flash.media.Sound;
	import flash.media.SoundChannel;
	import flash.utils.ByteArray;
	
	/**
	 * some code from andre michelle:
	 * http://blog.andre-michelle.com/2009/pitch-mp3/
	 * and engine from popforge (also andre michelle & joe ebert)
	 * adpated by gareth williams (all respect goes to original author/s)
	 */
	
	public class MP3Pitch implements Player
	{
		private const BLOCK_SIZE: int = 5072;
		
		//private var _mp3: Sound;
		private var decoder: Decoder;
		private var _sound: Sound;
		private var _soundRef:SoundChannel;
		
		private var _target: ByteArray;
		
		private var _position: Number; //keep track of position in mp3 streaming
		private var _rate: Number;
		
		public function MP3Pitch(decoder:Decoder)
		{
			this.decoder = decoder;
			_target = new ByteArray();

			_position = 0.0;
			_rate = 0.5; //22050 at 44100 is 0.5

			_sound = new Sound();
			_sound.addEventListener( SampleDataEvent.SAMPLE_DATA, this.sampleData );
		}
		private function sampleData(event:SampleDataEvent):void
		{
			trace(_target.length);
			
			//-- REUSE INSTEAD OF RECREATION
			var remainder:int = _target.bytesAvailable;//shuffle unread bytes to beginning of buffer
			if (remainder) 
			{ 
				_target.readBytes(_target, 0, remainder);
				_target.position = remainder;//write from here
			}
			
			//-- SHORTCUT
			var data: ByteArray = event.data;
			
			var scaledBlockSize: Number = BLOCK_SIZE * _rate;
			var positionInt: int = int(_position);
			var alpha: Number = _position - positionInt;//space between fractional and real read positions
			
			var positionTargetNum: Number = alpha;//next position (fractional)
			var positionTargetInt: int = -1;//next position (real)
			
			//-- COMPUTE NUMBER OF SAMPLES NEED TO PROCESS BLOCK (+2 FOR INTERPOLATION) - will read 2 samples past end of block
			var need: int = Math.ceil( scaledBlockSize ) + 2;
			
			//-- EXTRACT SAMPLES
			var read:int;
			try 
			{
				read = decoder.extract( _target, need, -1 );
			}
			catch(err:Error)
			{
				//either too many failed reads or end of file either way close
				_sound.removeEventListener( SampleDataEvent.SAMPLE_DATA, this.sampleData );
				return;
			}
			//var read:int = _mp3.extract( _target, need, positionInt );
			
			//number of samples depends on number of frames retrieved from audio tag in flv
			//it will be the nearest boundary following scaledBlockSize samples
			//var n: int = read == need ? BLOCK_SIZE : read / _rate; //would not have expected to accomodate greater read sizes ?
/* debug */	var n:int = read;
			
			//trace(need, "samples requested", read, "samples supplied", n, "samples buffered");
			
			var l0: Number;
			var r0: Number;
			var l1: Number;
			var r1: Number;
			
			for( var i: int = 0 ; i < n ; ++i)
			{
				//-- AVOID READING EQUAL SAMPLES, IF RATE < 1.0 (ie interpolate samples)
				if( int( positionTargetNum ) != positionTargetInt ) 
				{
					positionTargetInt = positionTargetNum;
					
					//-- SET TARGET READ POSITION
					_target.position = positionTargetInt << 3;
					
					//-- READ TWO STEREO SAMPLES FOR LINEAR INTERPOLATION
					l0 = _target.readFloat();
					r0 = _target.readFloat();
					
					l1 = _target.readFloat();
					r1 = _target.readFloat();
				}
				
				//-- WRITE INTERPOLATED AMPLITUDES INTO STREAM
				data.writeFloat( l0 + alpha * ( l1 - l0 ) );
				data.writeFloat( r0 + alpha * ( r1 - r0 ) );
				
				//-- INCREASE TARGET POSITION
				positionTargetNum += _rate;
				
				//-- INCREASE FRACTION AND CLAMP BETWEEN 0 AND 1
				alpha += _rate;
				while( alpha >= 1.0 ) --alpha;
			}
			
			//-- FILL REST OF STREAM WITH ZEROs - why is this necessary ?
			if( i < BLOCK_SIZE )
			{
				while( i < BLOCK_SIZE )
				{
					data.writeFloat( 0.0 );
					data.writeFloat( 0.0 );
					
					++i;
				}
			}
			
			//-- INCREASE SOUND POSITION
			_position += scaledBlockSize;
			
		}
		//interface
		public function get rate(): Number
		{
			return _rate;
		}
		public function set rate( value: Number ): void
		{
			if( value < 0.0 )
				value = 0;

			_rate = value;
		}
		public function start(): void
		{
			_soundRef = _sound.play();
		}
		public function stop(): void
		{
			_soundRef.stop();
		}
	}
}